#include "object/hiString.hpp"
#include "object/hiList.hpp"
#include "runtime/traceback.hpp"
#include "runtime/frameObject.hpp"
#include "memory/oopClosure.hpp"

StackElementKlass* StackElementKlass::_instance = NULL;
TracebackKlass* TracebackKlass::_instance = NULL;

StackElementKlass* StackElementKlass::get_instance()
{
    if (_instance == NULL) {
        _instance = new StackElementKlass();
    }

    return _instance;
}

void StackElementKlass::print(HiObject* x)
{
    StackElement* xse = (StackElement*)x;
    printf("  File \"");
    xse->_file_name->print();
    printf("\", line %d,", xse->_line_no);
    printf(" in ");
    xse->_func_name->print();
    printf("\n");
}

size_t StackElementKlass::size()
{
    return sizeof(StackElement);
}

StackElement::StackElement(HiString* file_name, HiString* func_name, int line_no):
    _file_name(file_name),
    _func_name(func_name),
    _line_no(line_no)
{
    set_klass(StackElementKlass::get_instance());
}

TracebackKlass* TracebackKlass::get_instance()
{
    if (_instance == NULL) {
        _instance = new TracebackKlass();
    }

    return _instance;
}

void TracebackKlass::print(HiObject* x)
{
    Traceback* tbx = (Traceback*)x;

    printf("Traceback (most recent call last):\n");
    for (int i = tbx->_stack_elements->size() - 1; i >= 0; i--) {
        tbx->_stack_elements->get(i)->print();
    }
}

void TracebackKlass::oops_do(OopClosure* f, HiObject* obj)
{
    f->do_oop((HiObject**)&(((Traceback*)obj)->_stack_elements));
}

void StackElementKlass::oops_do(OopClosure* f, HiObject* obj)
{
    f->do_oop((HiObject**)&(((StackElement*)obj)->_file_name));
    f->do_oop((HiObject**)&(((StackElement*)obj)->_func_name));
}

size_t TracebackKlass::size()
{
    return sizeof(Traceback);
}

Traceback::Traceback()
{
    _stack_elements = new HiList();
    set_klass(TracebackKlass::get_instance());
}

void Traceback::record_frame(FrameObject* frame)
{
    _stack_elements->append(
        new StackElement(frame->file_name(), frame->func_name(), frame->lineno())
    );
}